/** @file md5_object.cpp
    @brief MD5 model object */

#include "md5/md5_object.h"

using namespace G3;

//==============================================================================
// Constructor(s)
//==============================================================================
G3::MD5_OBJECT::MD5_OBJECT () {
    model = NULL;
    skeleton = NULL;
    animation = NULL;

    curFrame = 0;
    nextFrame = 0;

    maxTime = 0;
    lastTime = 0;

    log = &G3::MainLog;
}

//==============================================================================
// Destructor(s)
//==============================================================================
G3::MD5_OBJECT::~MD5_OBJECT () {
    if (model) {
        delete model;
        model = NULL;
    }
    if (skeleton) {
        delete skeleton;
        skeleton = NULL;
    }
}

//==============================================================================
// Loads a *.md5_mesh file
//==============================================================================
bool G3::MD5_OBJECT::LoadMesh (const std::string &filename) {
    if (!model)
        model = new MD5_MODEL ();

    if (!model) {
        if (log)
            log->Report ("ERROR! MD5_OBJECT::LoadMesh: Failed to allocate memory for a model..\n");
        return false;
    }

    if (model->LoadMesh (filename)) {
        delete skeleton;

        if (model->baseSkeleton) {
            // Rebuild animated skeleton with model's base skeleton
            skeleton = model->baseSkeleton->Clone ();
        }
        return true;
    }
    return false;
}

//==============================================================================
// Loads a *.md5_anim file
//==============================================================================
bool G3::MD5_OBJECT::LoadAnimation (const std::string &filename, const std::string &name) {
    if (!model)
        return false;

    return model->LoadAnimation (filename, name);
}

//==============================================================================
// Sets the animation
//==============================================================================
void G3::MD5_OBJECT::SetAnimation (const std::string &animName) {
    if (model) {
        // Retrieve animation from model's animation list
        animation = model->GetAnimation (animName);

        if (animation) {
            // Compute max frame time and reset lastTime
            maxTime = 1.0 / static_cast<double> (animation->framerate);
            lastTime = 0.0;
            // Reset current and next frames
            curFrame = 0;
            nextFrame = (curFrame + 1) % animation->numFrames;
        } else {
            delete skeleton;

            if (model->baseSkeleton) {
                // Rebuild animated skeleton with model's base skeleton
                skeleton = model->baseSkeleton->Clone ();
            }
        }
    }
}

//==============================================================================
// Animates the model
//==============================================================================
void G3::MD5_OBJECT::Animate (double elapsedTime) {
    // Only animate if there are any animations
    if (animation) {
        lastTime += elapsedTime;
        // Move to the next frame?
        if (lastTime >= maxTime) {
            lastTime = 0.0;

            curFrame = (curFrame + 1) % animation->numFrames;
            nextFrame = (curFrame + 1) % animation->numFrames;
        }
    }
}

//==============================================================================
// Prepares the model for rendering
//==============================================================================
void G3::MD5_OBJECT::Prepare () {
    if (model) {
        if (animation) {
            float factor = lastTime * animation->framerate;

            animation->Interpolate (curFrame, nextFrame, factor, skeleton);
        } else {
            delete skeleton;

            if (model->baseSkeleton) {
                // Rebuild animated skeleton with model's base skeleton
                skeleton = model->baseSkeleton->Clone ();
            }
        }

        model->Prepare (skeleton);
    }
}

//==============================================================================
// Renders the model
//==============================================================================
void G3::MD5_OBJECT::Render (double elapsedTime) {
    if (!model)
        return;

    Animate (elapsedTime);
    Prepare ();

    model->Render ();
}
